package com.lagou.edu.factory;

import com.lagou.edu.factory.annotation.CustomAutowired;
import com.lagou.edu.factory.annotation.CustomRepository;
import com.lagou.edu.factory.annotation.CustomService;
import com.lagou.edu.factory.annotation.CustomTransactional;
import com.lagou.edu.utils.AnnotationUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 一级缓存（存放成型 Bean）
     */
    private static Map<String, Object> map = new HashMap<>();

    /**
     * 二级缓存（从三级缓存中扩展来的 Bean）
     */
    private static Map<String, Object> earlyMap = new HashMap<>();

    /**
     * 三级缓存（提前曝光 Bean）
     */
    private static Map<String, ObjectFactory<?>> factoryMap = new HashMap<>();


    static {
        try {

            // 扫描包获取所有 Bean
            List<Class<?>> classList = AnnotationUtil.getFromPackage("com.lagou.edu");

            // 实例化所有 Bean
            for (Class<?> aClass : classList) {
                createBean(aClass, classList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public static Object getBean(String id) {
        return map.get(id);
    }

    /**
     * 实例化 Bean
     * @param aClass
     * @param classList
     * @return
     * @throws Exception
     */
    public static Object createBean(Class<?> aClass, List<Class<?>> classList) throws Exception {

        // 获取 BeanName
        String beanName = getBeanName(aClass);

        // 从一级缓存获取 Bean
        Object singleton = getSingleton(beanName);
        if (singleton == null) {
            // 没有则创建
            singleton = doCreatBean(aClass, beanName, classList);
        }

        return singleton;
    }

    /**
     * 真正的创建 Bean
     * @param aClass
     * @param beanName
     * @param classList
     * @return
     * @throws Exception
     */
    public static Object doCreatBean(Class<?> aClass, String beanName, List<Class<?>> classList) throws Exception {

        // 创建实例
        final Object obj = aClass.newInstance();

        // 将实例增强并放入到三级缓存中
        addSingletonFactory(beanName, () -> getEarlyBeanReference(aClass, obj));

        // 依赖注入
        populateBean(aClass, classList, obj);

        // 从三级缓存中获取 Bean 并放入到二级缓存中
        Object bean = getSingleton(beanName);

        // 把 Bean 放入到一级缓存中
        addSingleton(beanName, bean);

        return bean;
    }

    /**
     * 把 Bean 放入到一级缓存中
     * @param beanName
     * @param singletonObject
     */
    private static void addSingleton(String beanName, Object singletonObject) {
        synchronized (map) {
            map.put(beanName, singletonObject);
            factoryMap.remove(beanName);
            earlyMap.remove(beanName);
        }
    }

    /**
     * 依赖注入
     * @param aClass
     * @param classList
     * @param instance
     * @throws Exception
     */
    private static void populateBean(Class<?> aClass, List<Class<?>> classList, Object instance) throws Exception {

        // 使用反射获取所有属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {

            // 暴力获取
            field.setAccessible(true);
            CustomAutowired customAutowired = field.getAnnotation(CustomAutowired.class);
            if (customAutowired != null) {

                // 如果该属性被 @CustomAutowired 注解修饰
                if (customAutowired.value() != null && !"".equals(customAutowired.value())) {

                    // 如果 @CustomAutowired 注解起了别名，使用别名从缓存中获取 Bean
                    Object bean = getSingleton(customAutowired.value());
                    if (bean != null) {
                        field.set(instance, bean);
                        continue;
                    }
                }

                // 缓存中获取不到 Bean 则从 Bean 集合中查找
                Class<?> targetClass = getTargetClass(field.getType(), classList);
                // 创建 Bean
                Object bean = createBean(targetClass, classList);
                field.set(instance, bean);
            }
        }
    }

    /**
     * 从 Bean 集合中查找类
     * @param aClass
     * @param classList
     * @return
     */
    private static Class<?> getTargetClass(Class<?> aClass, List<Class<?>> classList) {
        for (Class<?> temp : classList) {
            if (temp == aClass) {
                return temp;
            }

            Class<?>[] interfaces = temp.getInterfaces();
            for (Class<?> face : interfaces) {
                if (face == aClass) {
                    return temp;
                }
            }
        }
        throw new RuntimeException("不支持的配置！");
    }

    /**
     * 增强
     * @param aClass
     * @param bean
     * @return
     */
    private static Object getEarlyBeanReference(Class<?> aClass, Object bean) {
        Object obj = bean;
        if (aClass.getAnnotation(CustomTransactional.class) != null) {

            // 如果被 @CustomTransactional 注解修饰，判断使用 jdk 代理还是 cglib 代理
            ProxyFactory proxyFactory = new ProxyFactory();
            if (aClass.isInterface() || Proxy.isProxyClass(aClass)) {

                // 如果是接口或者是代理类，则使用 jdk 代理
                obj = proxyFactory.getJdkProxy(bean);
            } else {

                // 否则使用 cglib 代理
                obj = proxyFactory.getCglibProxy(bean);
            }
        }
        return obj;
    }

    /**
     * 将实例增强并放入到三级缓存中
     * @param beanName
     * @param singletonFactory
     */
    private static void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        synchronized (map) {
            if (!map.containsKey(beanName)) {

                // 将 Bean 放入到三级缓存中，并删除二级缓存中的 Bean
                factoryMap.put(beanName, singletonFactory);
                earlyMap.remove(beanName);
            }
        }
    }

    /**
     * 从三级缓存中获取 Bean 并放入到二级缓存中
     * @param beanName
     * @return
     * @throws Exception
     */
    private static Object getSingleton(String beanName) throws Exception {

        // 先从一级缓存中获取
        Object singletonObject = map.get(beanName);
        if (singletonObject == null) {
            synchronized (map) {

                // 从二级缓存中获取
                singletonObject = earlyMap.get(beanName);
                if (singletonObject == null) {

                    // 从三级缓存中获取
                    ObjectFactory<?> singletonFactory = factoryMap.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();

                        // 把 Bean 放入二级缓存并删除三级缓存中的 Bean
                        earlyMap.put(beanName, singletonObject);
                        factoryMap.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    /**
     * 获取 BeanName
     * @param aClass
     * @return
     */
    private static String getBeanName(Class<?> aClass) {
        String beanName = aClass.getSimpleName();
        CustomService customService = aClass.getAnnotation(CustomService.class);
        CustomRepository customRepository = aClass.getAnnotation(CustomRepository.class);
        if (null != customService && null != customService.value() && !customService.value().isEmpty()) {
            beanName = customService.value();
        } else if (null != customRepository && null != customRepository.value() && !customRepository.value().isEmpty()) {
            beanName = customRepository.value();
        }
        return beanName;
    }
}
